package com.fire.chat.service.impl;


import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fire.chat.cache.AuthorityCacheStrategy;
import com.fire.chat.constant.CacheConstant;
import com.fire.chat.dao.SysMenuMapper;
import com.fire.chat.entity.bo.SysMenuBO;
import com.fire.chat.entity.po.SysMenu;
import com.fire.chat.entity.po.SysRoleMenuRef;
import com.fire.chat.entity.po.SysUserRoleRef;
import com.fire.chat.entity.vo.SysMenuVO;
import com.fire.chat.exception.BusinessException;
import com.fire.chat.service.SysMenuService;
import com.fire.chat.service.SysRoleMenuRefService;
import com.fire.chat.service.SysUserRoleRefService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * <p>标题: 菜单资源表 Service 实现类</p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2020</p>
 *
 * @version: 1.0
 * @author: xiejiarong
 * @date 2020-11-04
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Autowired(required = false)
    AuthorityCacheStrategy authorityCacheStrategy;

    @Autowired
    SysUserRoleRefService userRoleRefService;

    @Autowired
    SysRoleMenuRefService roleMenuRefService;

    @Autowired
    @Qualifier("taskExecutor")
    private ThreadPoolTaskExecutor executorService;




    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addMenu(SysMenuBO menu) {
        SysMenu sysMenu=new SysMenu();
        sysMenu.setMenuCode(menu.getMenuCode());
        sysMenu.setParentId(menu.getPid());
        sysMenu.setIconUrl(menu.getIcon());
        sysMenu.setNodeType(menu.getNodeType());
        sysMenu.setName(menu.getName());
        sysMenu.setIsDelete(0);
        //如果插入的当前节点为根节点，parentId指定-1
        if(menu.getPid() == -1){
            //根节点层级为1
            sysMenu.setLevel(1);
            //根节点路径为空
            sysMenu.setHttpPath("#");
        }else{
            SysMenu parentMenu = super.getOne(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getId,sysMenu.getParentId()).eq(SysMenu::getIsDelete,0),false);
            if(parentMenu == null){
                throw  new BusinessException("未查询到对应的父节点");
            }
            sysMenu.setLevel(parentMenu.getLevel() + 1);

        }
        super.save(sysMenu);
        if (authorityCacheStrategy!=null){
            authorityCacheStrategy.remove(CacheConstant.MENU_KEY);
        }

    }

    @Override
    public List<SysMenuVO> queryMenuTree() {
        List<SysMenuVO> result=null;
        if (authorityCacheStrategy!=null){
            result=new ArrayList<>(authorityCacheStrategy.<SysMenuVO>get(CacheConstant.MENU_KEY));
        }
        if (CollectionUtils.isEmpty(result)){
            List<SysMenu> list = super.list(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getIsDelete,0).orderByAsc(SysMenu::getLevel, SysMenu::getSort));
            result = transferMenuVo(list, CacheConstant.PARENT_ROOT_ID);
            if (authorityCacheStrategy!=null){
                authorityCacheStrategy.set(CacheConstant.MENU_KEY,new HashSet<>(result));
            }
        }
        return result;
    }

    /**
     *
     * @param userId
     * @return java.util.List<cn.com.hwqh.entity.vo.SysMenuVO>
     * @author xiejr
     * @description: 根据用户ID查询出所拥有的的权限，返回的是树形json
     * 需要注意的是，此方法主要为了方便前端渲染菜单列表界面,每次查询一定是数据库中最新的数据
     * 当涉及到用户权限的修改时，需要删除掉该用户在内存中的数据，因为本方法每次执行完之后都会判断内存中是否存在数据
     * 如果没有及时清除用户内存中的旧数据，可能出现前端已经为用户分配了权限，但是访问后端接口报无权限的问题
     * @date 2020/11/6 14:56
     */

    @Override
    public List<SysMenuVO> queryMenuByUserId(Long userId) {
        String key=String.format("%s:%s", CacheConstant.USER_KEY_PREFIX,userId.toString());
        //1、先查询当前用户对应的角色
        List<SysUserRoleRef> userRoles = userRoleRefService.list(Wrappers.<SysUserRoleRef>lambdaQuery().eq(SysUserRoleRef::getUserId,userId));
        List<SysMenuVO> resultList=new ArrayList<>();
        if(!CollectionUtils.isEmpty(userRoles)){
            Set<Long> menuIds = new HashSet<>();
            //2、通过角色查询菜单,注意有多个角色的情况，菜单资源需要去重
            for (SysUserRoleRef userRole : userRoles) {
                List<SysRoleMenuRef> roleMenus = roleMenuRefService.list(Wrappers.<SysRoleMenuRef>lambdaQuery().eq(SysRoleMenuRef::getRoleId,userRole.getRoleId()));
                if(!CollectionUtils.isEmpty(roleMenus)){
                    for (SysRoleMenuRef roleMenu : roleMenus) {
                        menuIds.add(roleMenu.getMenuId());
                    }
                    //查询对应的菜单
                    List<SysMenu> menus = super.list(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getIsDelete,0).in(SysMenu::getId,new ArrayList<>(menuIds)));
                    if(!CollectionUtils.isEmpty(menus)){
                        //将菜单下对应的父节点也一并全部查询出来
                        for (SysMenu menu : menus) {
                            menuIds.add(menu.getId());
                        }
                    }


                }
            }
            //用户拥有的角色权限不为空的情况下
            if (!menuIds.isEmpty()){
                //3、查询对应的所有菜单,并进行封装展示
                List<SysMenu> allMenus = super.list(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getIsDelete,0).in(SysMenu::getId, new ArrayList<>(menuIds)));
                resultList.addAll(transferMenuVo(allMenus, CacheConstant.PARENT_ROOT_ID));
                //最后判断一下当前缓存中是否不存在该用户的角色权限,这一步很重要，用于保证缓存数据与数据库一致
                //改为异步执行
                executorService.execute(()->{
                    if (authorityCacheStrategy!=null && !authorityCacheStrategy.hasKey(key)){
                        Set<String> menuCodes=new HashSet<>();
                        if (CollectionUtils.isNotEmpty(resultList)){
                            resultList.forEach(menu->menu.addPath(menuCodes));
                        }
                        authorityCacheStrategy.set(key,menuCodes);
                    }
                });
            }

        }

        return resultList;
    }



    @Override
    public void deleteById(String id) {
        LambdaUpdateWrapper<SysMenu> deleteWrapper=Wrappers.<SysMenu>lambdaUpdate().set(SysMenu::getIsDelete,1).eq(SysMenu::getId,id).or().eq(SysMenu::getParentId,id);
        super.update(deleteWrapper);
        if (authorityCacheStrategy!=null){
            authorityCacheStrategy.remove(CacheConstant.MENU_KEY);
        }

    }


    /**
      *
      *  @param allMenu 所有菜单
      *  @param parentId 父ID
      * @return java.util.List<MenuVo> 菜单树形
      * @author xiejr
      * @description: 递归组装菜单
      * @date 2020/11/5 10:40
      */

    public List<SysMenuVO> transferMenuVo(List<SysMenu> allMenu, Long parentId){
        List<SysMenuVO> resultList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(allMenu)){
            for (SysMenu source : allMenu) {
                if(parentId.longValue() == source.getParentId().longValue()){
                    SysMenuVO menuVo = new SysMenuVO();
                    menuVo.setId(source.getId()).setIconUrl(source.getIconUrl()).setLevel(source.getLevel()).setLinkUrl(source.getLinkUrl())
                            .setMenuCode(source.getMenuCode()).setName(source.getName()).setNodeType(source.getNodeType())
                            .setParentId(source.getParentId()).setPath(source.getHttpPath());
                    //递归查询子菜单，并封装信息
                    List<SysMenuVO> childList = transferMenuVo(allMenu, source.getId());
                    if(!CollectionUtils.isEmpty(childList)){
                        menuVo.setChildMenu(childList);
                    }
                    resultList.add(menuVo);
                }
            }
        }
        return resultList;
    }
}
